# _*_ coding: utf-8 _*_
"""
author: lianGang
email: 1542652663@qq.com
"""

"""
1.改变对象的字符串表示
"""

#
# class Pari:
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y
#
#     def __repr_(self):
#         return 'Pari({0.x!r}, {0.y!r})'.format(self)
#
#     def __str__(self):
#         return '({0.x!s}, {0.y!s})'.format(self)
#
#
# p = Pari(3, 4)
#
# # 自定义字符串的格式化
# _format = {
#     'ymd': '{d.year}-{d.month}-{d.day}',
#     'mdy': '{d.month}/{d.day}/{d.year}',
#     'dmy': '{d.day}/{d.month}/{d.year}'
# }
#
#
# class Date:
#     def __init__(self, year, month, day):
#         self.year = year
#         self.month = month
#         self.day = day
#
#     def __format__(self, code):
#         if code == '':
#             code = 'ymd'
#         fmt = _format[code]
#         return fmt.format(d=self)
#
#
# # d = Date(2012,12,21)
# # format(d)
#
# # 让对象支持上下文管理协议
# # 一个对象兼容with语句， 需要实现__enter__() 和 __exit__()方法
# from socket import socket, AF_INET, SOCK_STREAM
#
#
# class LazyConnection:
#     def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
#         self.address = address
#         self.family = family
#         self.type = type
#         self.sock = None
#
#     def __enter__(self):
#         if self.sock is not None:
#             raise RuntimeError('Already connected')
#         self.sock = socket(self.family, self.type)
#         self.sock.connect(self.address)
#         return self.sock
#
#     def __exit__(self, exc_type, exc_val, exc_tb):
#         self.sock.close()
#         self.sock = None
#
#
# from functools import partial
#
#
# # conn = LazyConnection(('www.python.org', 80))
# # # connection closed
# # with conn as s:
# #     # conn.__enter__() executes: connection open
# #     s.send(b'GET /index.html HTTP/1.0\r\n')
# #     s.send(b'Host: www.python.org\r\n')
# #     s.send(b'\r\n')
# #     resp = b''.join(iter(partial(s.recv, 8192), b''))
# #     # conn.__exit__() executes: connection closed
#
# # 上面的定义中一次只能允许一个socket连接
#
# class LazyConnection2:
#     def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
#         self.address = address
#         self.family = family
#         self.type = type
#         self.connections = []
#
#     def __enter__(self):
#         sock = socket(self.family, self.type)
#         sock.connect(self.address)
#         self.connections.append(sock)
#         return sock
#
#     def __exit__(self, exc_type, exc_val, exc_tb):
#         self.connections.pop().close()
#
#
# # conn2 = LazyConnection2(('www.python.org', 80))
# # with conn2 as s:
# #     pass
# #     with conn2 as s2:
# #         pass
#
#
# # 创建大量对象时节省内存方法
# # __slots__属性 主要是用来内存优化 使用后 不能给实例添加新属性
# class Date2:
#     __slots__ = ['year', 'month', 'day']
#
#     def __init__(self, year, month, day):
#         self.year = year
#         self.month = month
#         self.day = day
#
#
# # 在类中封装属性名
# class A:
#     def __init__(self):
#         self._internal = 0
#         self.public = 1
#
#     def public_method(self):
#         pass
#
#     def _internal_method(self):
#         pass
#
#
# class B:
#     def __init__(self):
#         self.__private = 0
#
#     def __private_method(self):
#         print('B private_method')
#         pass
#
#     def public_method(self):
#         pass
#         self.__private_method()
#
#
# class C(B):
#     def __init__(self):
#         super().__init__()
#         self.__private = 1
#
#     def __private_method(self):
#         pass
#         print('A private_method')
#
#
# lambda_ = 20  # trailing _ to avoid clash with lambda keyword
#
#
# # 创建可管理的属性
# class Person:
#     def __init__(self, first_name):
#         self._first_name = first_name
#
#     @property
#     def first_name(self):
#         return self._first_name
#
#     @first_name.setter
#     def first_name(self, value):
#         if not isinstance(value, str):
#             raise TypeError('Expected a string')
#         self._first_name = value
#
#     @first_name.deleter
#     def first_name(self):
#         raise AttributeError("Can't delete attribute")
#
#
# # 在已存在的get和set方法基础上定义property
# class Person2:
#     def __init__(self, first_name):
#         self.set_first_name(first_name)
#         # self._first_name = first_name
#
#     def get_first_name(self):
#         return self._first_name
#
#     def set_first_name(self, value):
#         if not isinstance(value, str):
#             raise TypeError('Expected a string')
#         self._first_name = value
#
#     def del_first_name(self):
#         raise AttributeError("Cant't delete attribute")
#
#     # make a property from existing get/set methods
#     name = property(get_first_name, set_first_name, del_first_name)
#
#
# # 调用父类的方法
# # class A:
# #     def spam(self):
# #         print('A.spam')
# #
# # class B(A):
# #     def spam(self):
# #         print('B.spam')
# #         super(B, self).spam()
#
# # super 在__init__ 中确保父类正确初始化
# # class A:
# #     def __init__(self):
# #         self.x = 0
# #
# # class B(A):
# #     def __init__(self):
# #         super().__init__()
# #         self.y = 1
#
#
# # class Base:
# #     def __init__(self):
# #         print('Base.__init__')
# #
# # class A(Base):
# #     def __init__(self):
# #         Base.__init__(self)
# #         print('A.__init__')
# #
# # class B(Base):
# #     def __init__(self):
# #         Base.__init__(self)
# #         print('B.__init__')
# #
# # class C(A,B):
# #     def __init__(self):
# #         A.__init__(self)
# #         B.__init__(self)
# #         print('C.__init__')
# ## Base.__init__()被调用两次
#
# # 使用super 替换 __init__
# # class Base:
# #     def __init__(self):
# #         print('Base.__init__')
# #
# # class A(Base):
# #     def __init__(self):
# #         super().__init__()
# #         print('A.__init__')
# #
# # class B(Base):
# #     def __init__(self):
# #         super().__init__()
# #         print('B.__init__')
# #
# # class C(A,B):
# #     def __init__(self):
# #         super().__init__()
# #         print('C.__init__')
#
# #
# # class A:
# #     def spam(self):
# #         print('A.spam')
# #         super().spam()
# #
# #
# # class B:
# #     def spam(self):
# #         print('B.spam')
# #
# #
# # class C(A, B):
# #     def spam(self):
# #         print('C.spam')
# #         super(A, self).spam()   # 这个顺序是从左至右， super A 则继承 A后的方法
# #         pass
# #
# #
# # c = C()
# # c.spam()
#
#
# # 在子类中，扩展定义在父类中的property
# class Person:
#     def __init__(self, name):
#         self.name = name
#     # Getter function
#     @property
#     def name(self):
#         return self._name
#     # Setter function
#     @name.setter
#     def name(self, value):
#         if not isinstance(value, str):
#             raise TypeError('Expected a string')
#         self._name = value
#     # Deleter function
#     @name.deleter
#     def name(self):
#         raise AttributeError("Can't delete attribute")
#
#
# class SubPerson(Person):
#     @property
#     def name(self):
#         print('Getting name')
#         return super().name
#
#     @name.setter
#     def name(self, value):
#         print('Setting name to', value)
#         super(SubPerson, SubPerson).name.__set__(self, value)
#
#     @name.deleter
#     def name(self):
#         print('Deleting name')
#         super(SubPerson, SubPerson).name.__delete__(self)
#
# class SubPerson2(Person):
#     # # 错误写法
#     # @property
#     # def name(self):
#     #     print('Getting name')
#     #     return super().name
#
#     @Person.name.getter
#     def name(self):
#         print('Getting name')
#         return super().name
#
# # s = SubPerson2('Guido')
# # print(s.name)
#
#
# # 创建新的类或实例属性 描述器
# # 描述器只能在类级别被定义， 而不能给实例单独定义
# # decriptor attribute for an interget type-checked attribute
# class Interget:
#     def __init__(self, name):
#         self.name = name
#
#     def __get__(self, instance, cls):
#         if instance is None:
#             return self
#         else:
#             return instance.__dict__[self.name]
#
#     def __set__(self, instance, value):
#         if not isinstance(value, int):
#             raise TypeError('Expected an int')
#         instance.__dict__[self.name] = value
#
#     def __delete__(self, instance):
#         del instance.__dict__[self.name]
#
#
# # class Point:
# #     x = Interget('x')
# #     y = Interget('y')
# #
# #     def __init__(self, x, y):
# #         self.x = x
# #         self.y = y
#
#
# # p = Point(2,3)
# # print(p.x)
#
# # 使用延迟计算属性
# class lazyproperty:
#     """延迟属性的 描述器
#      小缺陷: 计算出的值被创建后可以被修改
#     """
#     def __init__(self, func):
#         self.func = func
#
#     def __get__(self, instance, cls):
#         print(instance, cls)
#         if instance is None:
#             print('self:', self)
#             return self
#         else:
#             value = self.func(instance)
#             setattr(instance, self.func.__name__, value)
#             return value
#
#
# def lazyproperty2(func):
#     name = '_lazy_' + func.__name__
#     @property
#     def lazy(self):
#         if hasattr(self, name):
#             return getattr(self, name)
#         else:
#             value = func(self)
#             setattr(self, name, value)
#             return value
#     return lazy
#
# import math
#
# class Circle:
#     def __init__(self, radius):
#         self.radius = radius
#
#     # # 方式1
#     # @lazyproperty
#     # def area(self):
#     #     print("Computer area")
#     #     return math.pi *self.radius**2
#     #
#     # @lazyproperty
#     # def perimeter(self):
#     #     print('Computer perimeter')
#     #     return 2 * math.pi * self.radius
#
#     # 方式二
#     @lazyproperty2
#     def area(self):
#         print("Computer area")
#         return math.pi *self.radius**2
#
#     @lazyproperty2
#     def perimeter(self):
#         print('Computer perimeter')
#         return 2 * math.pi * self.radius
#
#
# # c = Circle(4.0)
# # print(c.radius)
# # print(c.perimeter)
# # print(c.area)
#
#
# # 简化数据结构的初始化
# # 方案 在一个基类中 实现公用的__init__()函数
# import math
#
# class Structure1:
#     _fields = []
#
#     def __init__(self, *args):
#         if len(args) != len(self._fields):
#             raise TypeError('Expected {} arguments'.format(len(self._fields)))
#         for name, value in zip(self._fields, args):
#             setattr(self, name, value)
#
# # example class definations
# # class Stock(Structure1):
# #     _fields = ['name', 'shares', 'price']
#
#
# class Point(Structure1):
#     _fields = ['x', 'y']
#
#
# class Circle2(Structure1):
#     _fields =  ['radius']
#
#     def area(self):
#         return math.pi*self.radius**2
#
#
# # s = Stock('ACME', 50, 91)
# # p = Point(2,3)
# # c = Circle2(4.5)
#
#
# # 支持关键字参数
# class Structure2:
#     _fields = []
#
#     def __init__(self, *args, **kwargs):
#         if len(args) > len(self._fields):
#             raise TypeError('Expected {} arguments'.format(len(self._fields)))
#         # set all of the positional arguments
#         for name, value in zip(self._fields, args):
#             setattr(self, name, value)
#
#         # set the remaining keyword arguments
#         for name in self._fields[len(args):]:
#             setattr(self, name, kwargs.pop(name))
#
#         # check for any remaining unknown arguemtns
#         if kwargs:
#             raise TypeError('Invalid arguments(s): {}'.format(','.join(kwargs)))
#
#
# # 定义接口或者抽象基类 例如通过执行类型检查确保子类实现了某些特定的方法
# from abc import ABCMeta, abstractmethod
#
# class IStream(metaclass=ABCMeta):
#     @abstractmethod
#     def read(self, maxbytes=-1):
#         pass
#
#     @abstractmethod
#     def write(self, data):
#         pass
#
#
# # 实现数据模型的类型约束
# base class uses a descriptor to set a value
class Descriptor:
    def __init__(self, name=None, **opts):
        self.name = name
        for key, value in opts.items():
            setattr(self, key, value)

    def __set__(self, instance, value):
        instance.__dict__[self.name] = value

# # descriptor for enforcing types
# class Typed(Descriptor):
#     expected_type = type(None)
#
#     def __set__(self, instance, value):
#         if not isinstance(value, self.expected_type):
#             raise TypeError('expected ' + str(self.expected_type))
#         super().__set__(instance, value)
#
# # descriptor for enforcing values
# class Unsigned(Descriptor):
#     def __set__(self, instance, value):
#         # print('value', value)
#         if value < 0:
#             raise ValueError('Expected >= 0')
#         super().__set__(instance, value)
#
# class MaxSized(Descriptor):
#     def __init__(self, name=None, **opts):
#         if 'size' not in opts:
#             raise TypeError('missing size option')
#         super().__init__(name, **opts)
#
#     def __set__(self, instance, value):
#         if len(value) >= self.size:
#             raise ValueError('size must be < ' + str(self.size))
#         super().__set__(instance, value)
#
#
# # 定义不同数据类型
# class Integer(Typed):
#     expected_type = int
#
# class UnsignedInteger(Integer, Unsigned):
#     pass
#
# class Float(Typed):
#     expected_type = float
#
# class UnsignedFloat(Float, Unsigned):
#     pass
#
# class String(Typed):
#     expected_type = str
#
# class SizedString(String, MaxSized):
#     pass
#
# # 使用上述自定义数据类型 自定义一个类
# class Stock:
#     # special constrains
#     name = SizedString('name', size=8)
#     shares = UnsignedInteger('shares')
#     price = UnsignedFloat('price')
#
#     def __init__(self, name, shares, price):
#         self.name = name
#         self.shares = shares
#         self.price = price
#
#
# # test
# # s = Stock('ACME', 75, 10.0)
#
# # 简化上述代码的方式 使用类装饰器
# # Class decorator to apply constraints
# def check_attributes(**kwargs):
#     def decorate(cls):
#         for key, value in kwargs.items():
#             if isinstance(value, Descriptor):
#                 value.name = key
#                 setattr(cls, key, value)
#             else:
#                 setattr(cls, key, value(key))
#         return cls
#
#     return decorate
#
# @check_attributes(name=SizedString(size=8),
#                   shares=UnsignedInteger,
#                   price= UnsignedFloat)
# class Stock2:
#     def __init__(self, name, shares, price):
#         self.name = name
#         self.shares = shares
#         self.price = price
#
# # s2 = Stock2('asdf', 20, 1.1)
#
# # 简化方式二 使用元类
# # A metaclass that applies checking
# class checkmeta(type):
#     def __new__(cls, clsname, bases, methods):
#         # Attach attribute names to the descriptors
#         for key, value in methods.items():
#             if isinstance(value, Descriptor):
#                 value.name = key
#         return type.__new__(cls, clsname, bases, methods)
#
#
# class Stock3(metaclass=checkmeta):
#     name = SizedString(size=8)
#     shares = UnsignedInteger()
#     price = UnsignedFloat()
#
#     def __init__(self, name, shares, price):
#         self.name = name
#         self.shares = shares
#         self.price = price

# s3 = Stock3('abc1', 123, 1.0)

# 方法四 使用装饰器作为混入类的替代
def Typed4(expected_type, cls=None):
    if cls is None:
        return lambda cls: Typed4(expected_type, cls)
    super_set = cls.__set__

    def __set__(self, instance, value):
        if not isinstance(value, expected_type):
            raise TypeError('expected ' + str(expected_type))
        super_set(self, instance, value)

    cls.__set__ = __set__
    return cls

def Unsigned4(cls):
    super_set = cls.__set__

    def __set__(self, instance, value):
        print(instance, value, 'Unsigned4')
        if value < 0:
            raise ValueError('Expected >=0')
        super_set(self, instance, value)

    cls.__set__ = __set__
    return cls


def MaxSized4(cls):
    super_init = cls.__init__

    def __init__(self, name=None, **opts):
        if 'size' not in opts:
            raise TypeError('missing size option')
        super_init(self, name, **opts)

    cls.__init__ = __init__
    super_set = cls.__set__

    def __set__(self, instance, value):
        if len(value) >= self.size:
            raise ValueError('size must be < ' + str(self.size))
        super_set(self, instance, value)

    cls.__set__ = __set__
    return cls


# special descriptors
@Typed4(int)
class Integer4(Descriptor):
    pass

@Unsigned4
class UnsignedInteger4(Integer4):
    pass

@Typed4(float)
class Float4(Descriptor):
    pass

@Unsigned4
class UnsignedFloat4(Float4):
    pass

@Typed4(str)
class String4(Descriptor):
    pass

@MaxSized4
class SizedString4(String4):
    pass


class Stock4:
    name = SizedString4(size=8)
    shares = UnsignedInteger4()
    # price = UnsignedFloat4()

    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        # self.price = price

s4 = Stock4('abc4', 10, 1.1)
# 运行异常未定位到问题点